package ru.stepanovdn.trading.server.service.order

import jakarta.annotation.PostConstruct
import jakarta.transaction.Transactional
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.domain.Sort
import org.springframework.stereotype.Service
import ru.stepanovdn.trading.server.entity.Candle
import ru.stepanovdn.trading.server.entity.Order
import ru.stepanovdn.trading.server.repository.OrderRepository
import ru.stepanovdn.trading.server.service.dictionary.InstrumentService
import ru.stepanovdn.trading.server.service.dictionary.InstrumentService.Instrument
import ru.stepanovdn.trading.server.service.tinkoff.ITinkoffOrderAPI
import ru.stepanovdn.trading.server.service.tinkoff.ITinkoffOrderAPI.OrderResult
import ru.stepanovdn.trading.server.strategy.AStrategy
import java.time.OffsetDateTime
import java.util.concurrent.CopyOnWriteArrayList

@Service
class OrderService (
    @Autowired val orderRepository: OrderRepository,
    @Autowired val instrumentService: InstrumentService,
    @Autowired val tinkoffOrderAPI: ITinkoffOrderAPI,
) {


    @Volatile
    private var orders: MutableList<Order?>? = null

    fun findActiveByFigiAndStrategy(figi: String?, strategy: AStrategy): Order? {
        return orders!!.stream()
            .filter { o: Order? -> figi == null || o!!.figi.equals(figi) }
            .filter { o: Order? -> o!!.sellDateTime == null }
            .filter { o: Order? ->
                o!!.strategy.equals(strategy.name)
            }
            .findFirst().orElse(null)
    }

    fun findClosedByFigiAndStrategy(figi: String?, strategy: AStrategy): List<Order?> {
        return orders!!.stream()
            .filter { o: Order? -> o!!.figi.equals(figi) }
            .filter { o: Order? -> o!!.sellDateTime != null }
            .filter { o: Order? ->
                o!!.strategy.equals(strategy.name)
            }
            .toList()
    }

    fun findLastByFigiAndStrategy(figi: String?, strategy: AStrategy): Order? {
        return orders!!.stream()
            .filter { o: Order? -> figi == null || o!!.figi.equals(figi) }
            .filter { o: Order? -> o!!.sellDateTime != null }
            .filter { o: Order? ->
                o!!.strategy.equals(strategy.name)
            }
            .reduce { first: Order?, second: Order? -> second }
            .orElse(null)
    }

    @Transactional(Transactional.TxType.REQUIRES_NEW)
    @Synchronized
    fun buy(candle: Candle, strategy: AStrategy): Order {
        val instrument: Instrument? = instrumentService.getInstrument(candle.figi!!)

        val lots = strategy.getCount(candle.figi)
        val result: OrderResult = tinkoffOrderAPI.buy(instrument!!, candle.closingPrice!!, lots!!)

        var order = Order(
            currency = instrument.currency,
            figi = instrument.figi,
            figiTitle = instrument.name,
            purchasePrice = result.price,
            strategy = strategy.name,
            purchaseDateTime = candle.dateTime,
            lots = strategy.getCount(candle.figi),
            purchaseCommission = result.commission
        )

        order = orderRepository.save(order)

        orders!!.add(order)
        return order
    }

    @Transactional(Transactional.TxType.REQUIRES_NEW)
    @Synchronized
    fun sell(candle: Candle, strategy: AStrategy): Order? {
        val instrument: Instrument? = instrumentService.getInstrument(candle.figi!!)
        var order: Order? = findActiveByFigiAndStrategy(candle.figi, strategy)

        val result: OrderResult = tinkoffOrderAPI.sell(instrument!!, candle.closingPrice!!, order!!.lots!!)
        order.sellDateTime = OffsetDateTime.now()
        order.sellCommission = result.commission
        order.sellPrice = if (result.price == null) candle.closingPrice else result.price
        order.sellProfit = result.price!!.subtract(order.purchasePrice)
        order = orderRepository.save(order)

        val orderId: Long? = order.id
        val orderInList: Order? = orders!!.stream()
            .filter { o: Order? -> o!!.id == orderId }.findFirst()
            .orElseThrow()

        orders!!.remove(orderInList)
        orders!!.add(order)
        return order
    }

    @PostConstruct
    fun loadOrdersFromDB() {
        orders = CopyOnWriteArrayList<Order>()
        orders!!.addAll(orderRepository.findAll(Sort.by("id")))
    }
}
